#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "HT_info.h"

#define BLOCK_SIZE 512

char* filetype;
int num_of_recs;

unsigned int hash(unsigned char *);

typedef struct
{
	int id;
	char name[15];
	char surname[20];
	char city[10];
} Record;

int HP_CreateFile(char *fileName)
{
	
	void* block;
	char flag=0;
	int fileDesc;

	
	if (BF_CreateFile(fileName)<0)
	{
		BF_PrintError("Cannot Create File");
		return -1;
	}
	
	if (fileDesc = BF_OpenFile(fileName)<0)
	{
		BF_PrintError("Cannot Open File");
		return -1;
	}
	
	if (BF_AllocateBlock(fileDesc)<0)
	{
		BF_PrintError("Cannot Allocate File");
		return -1;
	}
		
	if (BF_ReadBlock(fileDesc,0,&block)<0)
	{
		BF_PrintError("Cannot Read File");
		return -1;
	}
	
	memcpy(block, &flag, sizeof(char));				/*flag: number of entries, 0 */

	if (BF_WriteBlock(fileDesc,0) < 0)
	{
		BF_PrintError("Cannot Write File");
		return -1;
	}
	
	if (BF_CloseFile(fileDesc) < 0)
	{
		BF_PrintError("Cannot Close File");
		return -1;
	}

	return 0;
}



int HP_OpenFile(char* fileName)
{
	int fileDesc;
	void* block;
	char flag = 0;
	
	if ( ( fileDesc = BF_OpenFile(fileName) ) < 0)
	{
		BF_PrintError("Cannot Open File");
		return -1;
	}
		
	if (BF_ReadBlock(fileDesc,0, &block) < 0)
	{
		BF_PrintError("Cannot Read File");
		return -1;
	}
	
	memcpy(&flag, block, sizeof(char));

	if (flag == 1)
	{
		BF_PrintError("This is not a heapfile");
		return -1;
	}
	return fileDesc;
}
						  
						  
						  
						  
int HP_CloseFile(int fileDesc) 
{
	
	if (BF_CloseFile(fileDesc) < 0)
	{
		BF_PrintError("Cannot Close File");
		return -1;
	}
	return 0;
}




int HP_InsertEntry(int fileDesc, Record record)
{
	
	void *block;
	int counter, blocknumber;
	char *pointer;
	Record *records;
	num_of_recs = (BLOCK_SIZE-sizeof(int))/sizeof(Record);

	if ( (blocknumber = BF_GetBlockCounter(fileDesc) -1 ) < 0 )
	{ 
		BF_PrintError("Cannot Get Block Count File");
		return -1;
	}
	if (BF_ReadBlock(fileDesc, blocknumber, &block)<0)
	{
		BF_PrintError("Cannot Read File");
		return -1;
	}
	
	counter = *(int *)block;								/*number of block entries*/
	
	if (blocknumber == 0)
	{
		if (BF_AllocateBlock(fileDesc)<0)
		{
			BF_PrintError("Cannot Allocate File");
			return -1;
		}
		if (BF_ReadBlock(fileDesc, ++blocknumber, &block)<0)
		{
			BF_PrintError("Cannot Allocate File");
			return -1;
		}
		counter = 1;
		*(int *)block = counter;
		pointer = (char *)block + sizeof(int);
		records = (Record *) pointer;
		*records = record;
	}
	else if (counter == num_of_recs)							/*the block is full*/
	{
		if (BF_AllocateBlock(fileDesc)<0)
		{
			BF_PrintError("Cannot Allocate File");
			return -1;
		}
		if (BF_ReadBlock(fileDesc, ++blocknumber, &block)<0)
		{
			BF_PrintError("Cannot Allocate File");
			return -1;
		}
		counter = 1;
		*(int *)block = counter;
		pointer = (char *)block + sizeof(int);
		records = (Record *) pointer;
		*records = record;
	}
	else 														/*insert entry*/	
	{
		pointer = (char *)block + sizeof(int) + counter * sizeof(Record);	
		records = (Record *) pointer;
		*records = record; 
		*(int *)block = ++counter;
	}
	
	if (BF_WriteBlock(fileDesc, blocknumber)<0)
	{ 
		BF_PrintError("Cannot Write File");
		return -1;
	}
	
	return 0;
}



void HP_GetAllEntries(int fileDesc, char* fieldName, void* value)
{
	int i, j, read_blocks, entries;
	char* pointer;
	Record record;
	void* block;
	int offset;

	
	int BlockCounter; 
	BlockCounter = BF_GetBlockCounter(fileDesc);
	
	if (BlockCounter < 0)
		BF_PrintError("Cannot Get Block Counter");
	
	read_blocks = 0;
	
	
	for(i = 1; i < BlockCounter; i++)					/*searh every block*/
	{
		if (BF_ReadBlock(fileDesc, i, &block))
		{
			BF_PrintError("Cannot Read Block");
			continue;									/*continue to the next block*/
		}
		   
		read_blocks++;									/*number of read blocks*/
		
		offset = sizeof(int);							/*first record*/
		
		memcpy(&entries, block, sizeof(int));			/*first int: number of records*/							
		   
		   
		for(j = 0; j < entries; j++)
		{
			memcpy(&record, block+offset, sizeof(Record));
			
			if(!strcmp(fieldName,"id"))					/*check key*/
			{
				if(record.id == *((int *)value))
					printf("%d %s %s %s\n", record.id, record.name, record.surname, record.city);
			}	
			else if(!strcmp(fieldName,"name"))
			{
				if(!strcmp(record.name, value))
					printf("%d %s %s %s\n", record.id, record.name, record.surname, record.city);
			}
			else if(!strcmp(fieldName, "surname"))	
			{
				if(!strcmp(record.surname, value))
					printf("%d %s %s %s\n", record.id, record.name, record.surname, record.city);
			}	
			else if(!strcmp(fieldName,"city"))
			{
				if(!strcmp(record.city, value))
					printf("%d %s %s %s\n", record.id, record.name, record.surname, record.city);
			}
							
			offset += sizeof(Record);					/*next record*/
		}
							
	}
	printf("%d Blocks read\n", read_blocks);
}




int HT_CreateIndex(char* fileName,char attrtype,char* attrname,int attrlength,long int buckets)
{
	void* block;
	char flag = 1;
	char* pointer;
	int i = 0, fileDesc, block_num;
	HT_info info;
	
	info.attrType = attrtype;
	strcpy(info.attrName, attrname);
	info.attrLength = attrlength;
	info.numBuckets = buckets;

	
	if (BF_CreateFile(fileName)<0)
	{
		BF_PrintError("Cannot Create File");
		return -1;
	}

	if (fileDesc = BF_OpenFile(fileName)<0)
	{
		BF_PrintError("Cannot Open File");
		return -1;
	}
	
	for (i=0; i<=buckets; i++)
	{
		if (BF_AllocateBlock(fileDesc)<0)
		{
			BF_PrintError("Cannot Allocate File");
			return -1;
		}
	}
		
	if (BF_ReadBlock(fileDesc,0,&block)<0)
	{
		BF_PrintError("Cannot Read File");
		return -1;
	}
		
	memcpy(block, &flag, sizeof(char));								/*Hash File*/
	memcpy(block+sizeof(char), &info, sizeof(HT_info));				/*Write index*/
	
	if (BF_WriteBlock(fileDesc,0)<0)
	{
		BF_PrintError("Cannot Write File");
		return -1;
	}
		
	if (BF_CloseFile(fileDesc)<0)
	{
		BF_PrintError("Cannot Close File");
		return -1;
	}
	return 0;
}


HT_info* HT_OpenIndex(char* fileName)
{
	
	void* block;
	HT_info* info;
	int fileDesc;
	char flag;
	
	info = malloc(sizeof(HT_info));
	
	if ((fileDesc = BF_OpenFile(fileName))<0)
	{
		BF_PrintError("Cannot Open File");
		return NULL;
	}
	if (BF_ReadBlock(fileDesc,0, &block)<0)
	{
		BF_PrintError("Cannot Read File");
		return NULL;
	}


	memcpy(&flag, block, sizeof(char));						/*type of file*/
	memcpy(info, block+sizeof(char), sizeof(HT_info));
	info->fileDesc = fileDesc;
	
	if (flag == 0)
	{
		BF_PrintError("This is not a hashfile");
		return NULL;
	}
	
	return info;
	
}



int HT_CloseIndex( HT_info* header_info )
{
	if (BF_CloseFile(header_info->fileDesc))
	{
		BF_PrintError("Cannot Close File");
		return -1;
	}
	free(header_info);
	return 0;
}
 
 
int HT_InsertEntry(HT_info header_info,Record record)
{
	int block_num, entries=0, temp;
	int * block_of;
	char* pointer;
	Record rec;
	Record* records;
	void* block;


	if(!strcmp(header_info.attrName,"id"))
	{
		char str[10];
		sprintf(str, "%d", record.id );
		block_num = hash(str)%header_info.numBuckets + 1;
	}		
	else if(!strcmp(header_info.attrName,"name"))
		block_num = hash(record.name)%header_info.numBuckets + 1;
	
	else if(!strcmp(header_info.attrName, "surname"))	
		block_num = hash(record.surname)%header_info.numBuckets + 1;
	
	else if(!strcmp(header_info.attrName,"city"))
		block_num = hash(record.city)%header_info.numBuckets + 1;
	
	temp = block_num;

	
	while (temp)
	{
		if (BF_ReadBlock(header_info.fileDesc, temp, &block) < 0)
		{
			BF_PrintError("Cannot Read Block");
			return -1;
		}

		memcpy(&entries,block,sizeof(int));
		
		/*check if block has free space for the record*/
		if ( (BLOCK_SIZE - entries*sizeof(Record) - 2*sizeof(int)) > sizeof(Record) )	
		{
			pointer = (void *)block + sizeof(int) + entries*sizeof(Record);
			memcpy(pointer, &record, sizeof(Record));
			entries++;
			memcpy(block, &entries, sizeof(int));
			block_num = temp;
			break;
		}
		else 					/*the block is full*/
		
		{
			block_num = temp;
			memcpy(&temp, block + BLOCK_SIZE - sizeof(int), sizeof(int));
			
			if (temp == 0)		/*overflow*/			
			{
				if(BF_AllocateBlock(header_info.fileDesc)<0)
				{
					BF_PrintError("Cannot Allocate File");
					return -1;
				}
				
				temp = BF_GetBlockCounter(header_info.fileDesc) - 1;
				memcpy(block + BLOCK_SIZE - sizeof(int), &temp, sizeof(int));
				if (BF_WriteBlock(header_info.fileDesc, block_num))
				{ 
					BF_PrintError("Cannot Write File");
					return -1;
				}
			
			
			}
		}
	}
	
	
	if (BF_WriteBlock(header_info.fileDesc, block_num))
	{ 
		BF_PrintError("Cannot Write File");
		return -1;
	}
	return 0;
}


void HT_GetAllEntries(HT_info header_info, void *value)
{
	int read_blocks = 0;
	int block_num, entries, i;
	int offset;
	void* block;
	Record* record;
	char a[10];
	record = malloc(sizeof(Record));
	
	if(!strcmp(header_info.attrName,"id")){
		
		sprintf(a,"%d",*(int*)value);
		value = a;
		
	}
	block_num = hash(value)%header_info.numBuckets + 1;
	
	while(block_num)				
	{
		if (!BF_ReadBlock(header_info.fileDesc, block_num, &block))
			BF_PrintError("Cannot Read Block");
				   
		read_blocks++;									//number of read blocks
		
		memcpy(&entries, block, sizeof(int));		//first int: number of records  
		for(i = 0; i < entries; i++)
		{
			memcpy(record, block+sizeof(int)+i*sizeof(Record), sizeof(Record));
			if(header_info.attrType == 'i')
			{
				if(record->id == *(int *)value)
					printf("%d %s %s %s\n", record->id, record->name, record->surname, record->city);
			}	
			else if(header_info.attrType == 'c')
			{
				if(!strcmp(header_info.attrName,"name"))
				{
				   if(!strcmp(record->name, value))
					  printf("%d %s %s %s\n", record->id, record->name, record->surname, record->city);
				}
				else if(!strcmp(header_info.attrName, "surname"))	
				{
					if(!strcmp(record->surname, value))
						printf("%d %s %s %s\n", record->id, record->name, record->surname, record->city);
					  
					else if(!strcmp(header_info.attrName,"city"))
					{
						if(!strcmp(record->city, value))
							printf("%d %s %s %s\n", record->id, record->name, record->surname, record->city);
					}
				}
			}
		}
		block_num = *(int *)block + BLOCK_SIZE - sizeof(int);	/*next block != 0*/
	}
	printf("%d Blocks read\n",read_blocks);
}

unsigned int hash(unsigned char *str)
{
	unsigned long hash = 5381;
	int c;
	while (c = *str++)
		hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
	
	return hash;
}
